#include    "sendrtmp.h"

#define msleep(x) usleep(x*1000)

 
int 
sendpacket(RTMP *rtmp, unsigned int packettype, unsigned char *data, unsigned int size, unsigned int mtimestamp)
{
    int ret = -1;
    if (rtmp == NULL) return -1; 

    RTMPPacket packet;
    RTMPPacket_Reset(&packet);
    RTMPPacket_Alloc(&packet, size);
  
    packet.m_hasAbsTimestamp = 0;
    packet.m_packetType = packettype;
    packet.m_nChannel = 0x04;
    packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
    packet.m_nTimeStamp = mtimestamp;
    packet.m_nInfoField2 = rtmp->m_stream_id;
    packet.m_nBodySize = size;
    memcpy(packet.m_body, data, size);
  
    ret = RTMP_SendPacket(rtmp, &packet, 0);
    mp4u_log("rmtp sendpacket ret:%d  size:%u", ret, size);
  
    RTMPPacket_Free(&packet);
  
    return ret;
} 

typedef struct flvtaghead_s {
    u_char  type;
    u_char  datalen[3];
    u_char  timestamp[3];
    u_char  tsext;
    u_char  streamid[3];
} flvtaghead_t;

#define READ_BUF_LEN  1024*100

void skipsize(mp4u_buf_t *buf, uint32_t osize)
{
    int unparse     = buf->last - buf->pos;

    if (unparse < osize){
       buf->pos += osize;
    }else{
        buf->pos = buf->last = buf->start;
    }

    buf->file_pos += osize;
}

int
readdata(mp4u_buf_t *buf, uint32_t bodylen, u_char **data)
{
    int ret         = MP4U_OK, size = 0, osize = bodylen;
    int unparse     = buf->last - buf->pos;

    *data  = NULL;
    if (osize > READ_BUF_LEN){
        mp4u_log_error("the osize is bigger than READ_BUF_LEN, please increase READ_BUF_LEN size;");  
        return MP4U_ERROR;
    }

    if (unparse < osize){
        if (buf->file_pos == buf->file_last) return MP4U_DONE;
        buf->pos = buf->last = buf->start;
        size = mp4u_read_file(buf->file, buf->last, READ_BUF_LEN, buf->file_pos);
        if (size == MP4U_ERROR) {
            return MP4U_ERROR;
        }

        if (size == 0) {
            return MP4U_DONE;
        }
    
        buf->last += size;
        ret = size;
    }else{
        ret = osize;
    }
    
    *data = buf->pos;
    buf->pos += osize;
    buf->file_pos += osize;
    return ret; 
}

int
readflvtaghead(mp4u_buf_t *buf, flvtaghead_t **head)
{
    return readdata(buf, sizeof(flvtaghead_t), (u_char**)head);
}

int
readflvtagbody(mp4u_buf_t *buf, uint32_t bodylen, u_char  **body)
{
    return readdata(buf, bodylen, body);
}

int
readpretagsize(mp4u_buf_t *buf, uint32_t *size)
{
    int ret = MP4U_OK;
    u_char  *data;
    
    if ((ret=readdata(buf, sizeof(uint32_t), &data)) < MP4U_OK){
        mp4u_log_error("readdata error");
        return ret;
    }

    *size = mp4u_mp4_get_32value(data);
    mp4u_log("the tagsize is:%d", *size);
    return ret;
}


int
sendflvfile(RTMP *rtmp, const char *filename)
{
    static u_char   data[READ_BUF_LEN] = {0};
    int             ret = -1;
    uint32_t        packettype, bodylen, timestamp, tagsize, preframets, escape;
    struct timeval  timev;
    long            start, now;
    file_t          file;
    mp4u_buf_t      buf;
    flvtaghead_t   *head;
    u_char         *body;

    buf.start = data;
    buf.end   = data + READ_BUF_LEN;
    buf.pos = buf.last = buf.start;
    buf.file = &file;

    if ((ret=mp4u_init_file(&file, filename))!= MP4U_OK){
        mp4u_log_error("mp4u_init_file error:%d", ret);
        return ret;
    }

    buf.file_pos = 9 + 4;
    buf.file_last = file.size;
 
    preframets = 0;

    gettimeofday(&timev, NULL);

    start = 1000*timev.tv_sec + timev.tv_usec/1000; //ms
    while(1){
        gettimeofday(&timev, NULL);
        now = 1000*timev.tv_sec + timev.tv_usec/1000; //ms
        escape = now - start;
        mp4u_log("escape %d preframets:%d", escape, preframets);
        if (escape < preframets){
            msleep(500);
            continue;
        }

        if ((ret=readflvtaghead(&buf, &head)) < MP4U_OK){
            mp4u_log("read flvtaghead error or end of file %d", ret); 
            return ret;
        }

        if (head->tsext == 0){
            timestamp = mp4u_mp4_get_24value(head->timestamp);
        }else{
            int ts[4] = {0};
            ts[0] = head->tsext;
            ts[1] = head->timestamp[0];
            ts[2] = head->timestamp[1];
            ts[3] = head->timestamp[2];
            timestamp = mp4u_mp4_get_32value(ts);
        }

        bodylen = mp4u_mp4_get_24value(head->datalen);

        mp4u_log("type:%d tagbodylen:%d timestamp:%d", head->type, bodylen, timestamp);

        if (head->type == 8 || head->type == 9){
        //if (head->type == 9){
            
            if ((ret=readflvtagbody(&buf, bodylen, &body)) < MP4U_OK){
                mp4u_log("read flvtagbody error %d", ret); 
                return ret;
            }
     
            packettype = head->type == 8 ? RTMP_PACKET_TYPE_AUDIO:RTMP_PACKET_TYPE_VIDEO; 
            sendpacket(rtmp, packettype, body, bodylen, timestamp);
        }else{
            skipsize(&buf, bodylen);
        }

        readpretagsize(&buf, &tagsize);
        preframets = timestamp;
    }
    
    return ret;  
}

int 
main(int argc, char *const *argv)
{
    int ret = 0;
    RTMP_LogSetLevel(RTMP_LOGDEBUG);
    RTMP *rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);

    if((ret = RTMP_SetupURL(rtmp, (char*)argv[2])) < 0 ){
       mp4u_log_error("RTMP_SetupURL ret:%d error:%s", ret, strerror(errno));
       return -1;
    }

    RTMP_EnableWrite(rtmp);
    if((ret = RTMP_Connect(rtmp, NULL)) < 0 ){
       mp4u_log_error("RTMP_Connect ret:%d error:%s", ret, strerror(errno));
       return -1;
    }

    if((ret = RTMP_ConnectStream(rtmp,0)) < 0 ){
       mp4u_log_error("RTMP_ConnectStream ret:%d error:%s", ret, strerror(errno));
       return -1;
    }

    sendflvfile(rtmp, argv[1]);

    if(rtmp){
       RTMP_Close(rtmp);
       RTMP_Free(rtmp);  
       rtmp = NULL;  
    }

    return ret;
}
